using System;
using System.Collections;

namespace Meta {
	public class ListSetter : CallInterfaceProcedure, IArglist {
		int position;

		public ListSetter(int position, string name) {
			this.name = "set-"+name;
			this.position = position;
		}

		object[] Arglist {
			get {
				return new object[2] {"list", "new-value" };
			}
		}


		public override object Call(object list, object newValue) {
			IList l = list as IList;

			if (l==null)
				throw new ArgumentTypeException("list", list, typeof(IList));

			l[position] = newValue;

			return list;
		}
	}


	public class Lists {
		public static bool IsEmpty(IList l) {
			return l.Count==0;
		}

		public static IList Rest(object list) {
			LinkedList l = list as LinkedList;

			if (l!=null)
				return l.next;
			else {
				OffsetList o = list as OffsetList;
				if (o!=null)
					return o.Rest();
				else
					return new OffsetList((IList)list,1);
			}
		}

		public static LinkedList Cons(object item, IList rest) {
			return new LinkedList(item, rest);
		}
	}

	[Serializable]
	public class LinkedList : IList {
		public object item;
		public IList next;
		public LinkedList(object item, IList next) {
			this.item = item;
			this.next = next;
		}

		public object this[int index] {
			get {
				LinkedList p = this;
				while (index>0) {
					index--;
					LinkedList next = p.next as LinkedList;
					if (next!= null)
						p = next;
					else
						// Linked to a normal IList
						return p.next[index];
				}
				return p.item;
			}
			set {
				LinkedList p = this;
				while (index>0) {
					index--;
					LinkedList next = p.next as LinkedList;
					if (next!= null)
						p = next;
					else {
						// Linked to a normal IList
						p.next[index] = value;
						return;
					}
				}
				p.item = value;
			}
		}

		public void CopyTo(System.Array target, int start) {
			IList p = this;
			while (p != null) {
				LinkedList l = p as LinkedList;
				if (l != null) {
					target.SetValue(l.item, start);
					start++;
					p = l.next;
				} else {
					p.CopyTo(target, start);
					return;
				}
			}
		}

		public int Count {
			get {
				int i=0;
				IList p = this;
				while (p != null) {
					LinkedList l = p as LinkedList;
					if (l!=null) {
						i++;
						p = l.next;
					} else {
						return i+p.Count;
					}
				}
				return i;
			}
		}

		public bool IsSynchronized {
			get {
				return false;
			}
		}

		public object SyncRoot {
			get {
				throw new System.NotSupportedException("Can't synchronize linked lists (sorry)");
			}
		}

		public IEnumerator GetEnumerator() {
			return new LinkedListEnumerator(this);
		}

		public int Add(object x) {
			next = new LinkedList(x, next);
			return 1;
		}

		public void Clear() {
			throw new System.NotSupportedException("LinkedLists do not support the Clear() operation");
		}

		public bool Contains(object item) {
			return IndexOf(item)>=0;
		}

		public int IndexOf(object item) {
			int i=0;
			IList p = this;
			while (p != null) {
				LinkedList l = p as LinkedList;
				if (l!= null) {
					if (l.item == item)
						return i;
					p = l.next;
					i++;
				} else {
					int subindex = p.IndexOf(item);
					if (subindex>=0)
						return i+subindex;
					else
						return -1;
				}
			}
			return -1;
		}

		public void Insert(int position, object item) {
			throw new System.NotSupportedException("LinkedLists do not support the Insert() operation");
		}

		public bool IsFixedSize {
			get {
				return true;
			}
		}

		public bool IsReadOnly {
			get {
				return false;
			}
		}

		public void Remove(object ignore) {
			throw new System.NotSupportedException("LinkedLists do not support the Remove() operation");
		}

		public void RemoveAt(int ignore) {
			throw new System.NotSupportedException("LinkedLists do not support the Remove() operation");
		}

		class LinkedListEnumerator : IEnumerator {
			LinkedList list;
			LinkedList current=null;
			IEnumerator tailEnumerator=null;
			public LinkedListEnumerator(LinkedList list) {
				this.list = list;
				tailEnumerator=null;
			}

			public object Current {
				get {
					if (tailEnumerator!=null)
						return tailEnumerator.Current;
					else
						return current.item;
				}
			}

			public bool MoveNext() {
				if (tailEnumerator!=null)
					return tailEnumerator.MoveNext();
				if (current == null) {
					current = list;
					return true;
				} else {
					LinkedList n = current.next as LinkedList;
					if (n != null) {
						current = n;
						return true;
					} else {
						tailEnumerator = current.next.GetEnumerator();
						return tailEnumerator.MoveNext();
					}
				}
			}

			public void Reset() {
				current = null;
				tailEnumerator = null;
			}
		}
	}

	[Serializable]
	public class OffsetList : IList {
		IList list;
		int offset;
		public OffsetList(IList list, int offset) {
			this.list = list;
			this.offset = offset;
		}

		public OffsetList Rest() {
			return new OffsetList(list, offset+1);
		}

		public object this[int index] {
			get {
				return list[index+offset];
			}
			set {
				list[index+offset] = value;
			}
		}

		public void CopyTo(System.Array target, int start) {
			int n = Count;
			for (int i=0; i<n; i++)
				target.SetValue(list[i+offset], i+start);
		}

		public int Count {
			get {
				int c = list.Count;
				return c-offset;
			}
		}

		public bool IsSynchronized {
			get {
				return list.IsSynchronized;
			}
		}

		public object SyncRoot {
			get {
				return list.SyncRoot;
			}
		}

		public IEnumerator GetEnumerator() {
			return new OffsetListEnumerator(this);
		}

		System.NotSupportedException ReadOnly {
			get {
				return new System.NotSupportedException("OffsetLists are read-only");
			}
		}

		public int Add(object x) {
			throw ReadOnly;
		}

		public void Clear() {
			throw ReadOnly;
		}

		public bool Contains(object item) {
			return IndexOf(item)>=0;
		}

		public int IndexOf(object item) {
			int c=Count;
			for (int i=0; i<c; i++) {
				if (this[i]==item)
					return i;
			}
			return -1;
		}

		public void Insert(int position, object item) {
			throw ReadOnly;
		}

		public bool IsFixedSize {
			get {
				return true;
			}
		}

		public bool IsReadOnly {
			get {
				return true;
			}
		}

		public void Remove(object item) {
			throw ReadOnly;
		}

		public void RemoveAt(int position) {
			throw ReadOnly;
		}

		class OffsetListEnumerator : IEnumerator {
			int position=-1;
			OffsetList list;
			public OffsetListEnumerator(OffsetList list) {
				this.list = list;
			}

			public object Current {
				get {
					return list[position];
				}
			}

			public bool MoveNext() {
				position++;
				return position<list.Count;
			}

			public void Reset() {
				position = -1;
			}
		}
	}
}